using GrapeCity.Forguncy.Commands;
using GrapeCity.Forguncy.Plugin;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.parser;

namespace PdfOperationCommand
{
    [Icon("pack://application:,,,/PdfOperationCommand;component/Resources/Info.png")]
    [OrderWeight(1)]
    [Description("获取PDF文件的信息，使用iTextSharp实现")]
    public class PdfInformationServerCommand : BaseServerCommand, ICommandExecutableInServerSideAsync,
        IServerCommandParamGenerator
    {
        [Required]
        [FormulaProperty]
        [DisplayName("文件路径")]
        [Description("文件绝对路径，如：D:\\example.pdf(Windows)，/path/example.pdf(Linux)")]
        public object FilePath { get; set; }

        [DisplayName("PDF文件信息类型")]
        [Description("PDF文件信息类型，如果选择全部信息，则返回所有类型的信息")]
        public PdfInformationType PdfInformationType { get; set; }

        [DisplayName("PDF文件页码")]
        [Description("PDF文件页码（默认为 1，即第一页）")]
        [IntProperty(Min = 1)]
        [FormulaProperty]
        public Object PageIndex { get; set; } = 1;

        [OrderWeight(999)]
        [DisplayName("将结果返回到变量")]
        [ResultToProperty]
        [Required]
        public string ResultTo { get; set; }

        public async Task<ExecuteResult> ExecuteAsync(IServerCommandExecuteContext dataContext)
        {
            var filePath = await GetParamValue(dataContext, FilePath);
            var pageIndex = int.Parse((await GetParamValue(dataContext, PageIndex)).ToString());
            
            object value;
            using (var reader = new PdfReader(filePath.ToString()))
            {
                Rectangle pageSize;
                var pageCount = reader.NumberOfPages;
                if (pageCount < pageIndex)
                {
                    throw new Exception("PDF页码不能超过最大页数，PDF页数为：" + pageCount);
                }

                switch (PdfInformationType)
                {
                    case PdfInformationType.All:
                        var pdfInfo = new PdfInformation();
                        pageSize = reader.GetPageSizeWithRotation(pageIndex);
                        pdfInfo.Width = ConvertPointsToMillimeters(pageSize.Width);
                        pdfInfo.Height = ConvertPointsToMillimeters(pageSize.Height);
                        pdfInfo.PageCount = reader.NumberOfPages;
                        pdfInfo.Author = GetValueOrDefault(reader.Info, "Author", "Static");
                        pdfInfo.Title = GetValueOrDefault(reader.Info, "Title", "Static");
                        pdfInfo.Subject = GetValueOrDefault(reader.Info, "Subject", "Static");
                        pdfInfo.Creator = GetValueOrDefault(reader.Info, "Creator", "Static");
                        pdfInfo.CreateTime = GetValueOrDefault(reader.Info, "CreationDate", "Static");
                        pdfInfo.LastModifyTime = GetValueOrDefault(reader.Info, "ModDate", "Static");
                        pdfInfo.PageContent = PdfTextExtractor.GetTextFromPage(reader, pageIndex);
                        value = pdfInfo;
                        break;
                    case PdfInformationType.Width:
                        pageSize = reader.GetPageSizeWithRotation(pageIndex);
                        value = ConvertPointsToMillimeters(pageSize.Width);
                        break;
                    case PdfInformationType.Height:
                        pageSize = reader.GetPageSizeWithRotation(pageIndex);
                        value = ConvertPointsToMillimeters(pageSize.Height);
                        break;
                    case PdfInformationType.PageCount:
                        value = pageCount;
                        break;
                    case PdfInformationType.Author:
                        value = GetValueOrDefault(reader.Info, "Author", "Static");
                        break;
                    case PdfInformationType.Title:
                        value = GetValueOrDefault(reader.Info, "Title", "Static");
                        break;
                    case PdfInformationType.Subject:
                        value = GetValueOrDefault(reader.Info, "Subject", "Static");
                        break;
                    case PdfInformationType.Creator:
                        value = GetValueOrDefault(reader.Info, "Creator", "Static");
                        break;
                    case PdfInformationType.CreateTime:
                        value = GetValueOrDefault(reader.Info, "CreationDate", "Static");
                        break;
                    case PdfInformationType.LastModifyTime:
                        value = GetValueOrDefault(reader.Info, "ModDate", "Static");
                        break;
                    case PdfInformationType.PageContent:
                        value = PdfTextExtractor.GetTextFromPage(reader, pageIndex);
                        break;
                    default:
                        value = "";
                        break;
                }
            }

            dataContext.Parameters[ResultTo] = value;
            return new ExecuteResult();
        }

        private int ConvertPointsToMillimeters(float points)
        {
            return (int)Math.Round(points * 25.4f / 72);
        }

        private string GetValueOrDefault(Dictionary<string, string> dict, string key, string defaultValue)
        {
            dict.TryGetValue(key, out var @default);
            return @default ?? defaultValue;
        }

        public override string ToString()
        {
            return "获取PDF文件相关信息";
        }

        public override bool GetDesignerPropertyVisible(string propertyName, CommandScope commandScope)
        {
            if (propertyName == nameof(PageIndex))
            {
                return PdfInformationType == PdfInformationType.All || PdfInformationType == PdfInformationType.Width ||
                       PdfInformationType == PdfInformationType.Height ||
                       PdfInformationType == PdfInformationType.PageContent;
            }

            return base.GetDesignerPropertyVisible(propertyName, commandScope);
        }

        public IEnumerable<GenerateParam> GetGenerateParams()
        {
            if (PdfInformationType == PdfInformationType.All)
            {
                yield return new GenerateObjectParam()
                {
                    ParamName = ResultTo,
                    Description = "PDF文件相关信息",
                    SubPropertiesDescription = new Dictionary<string, string>
                    {
                        { "Width", "PDF页面宽度（mm）" },
                        { "Height", "PDF页面高度（mm）" },
                        { "PageCount", "PDF页面数量" },
                        { "Author", "PDF文档作者" },
                        { "Title", "PDF文档标题" },
                        { "Subtitle", "PDF文档副标题" },
                        { "CreateTime", "PDF文档创建时间" },
                        { "LastModifyTime", "PDF文档上次修改时间" },
                        { "PageContent", "PDF文档页面内容" },
                    }
                };
            }
            else
            {
                yield return new GenerateNormalParam()
                {
                    ParamName = ResultTo,
                    Description = "PDF文件相关信息"
                };
            }
        }
    }

    public enum PdfInformationType
    {
        [Description("全部信息")] All,
        [Description("宽度（mm）")] Width,
        [Description("高度（mm）")] Height,
        [Description("页面数量")] PageCount,
        [Description("作者")] Author,
        [Description("标题")] Title,
        [Description("主题")] Subject,
        [Description("创建者")] Creator,
        [Description("创建时间")] CreateTime,
        [Description("上次修改时间")] LastModifyTime,
        [Description("页面内容")] PageContent,
    }

    public class PdfInformation
    {
        [Description("宽度（mm）")] public int Width { get; set; }
        [Description("高度（mm）")] public int Height { get; set; }
        [Description("页面数量")] public int PageCount { get; set; }
        [Description("作者")] public string Author { get; set; }
        [Description("标题")] public string Title { get; set; }
        [Description("主题")] public string Subject { get; set; }
        [Description("创建者")] public string Creator { get; set; }
        [Description("创建时间")] public string CreateTime { get; set; }
        [Description("上次修改时间")] public string LastModifyTime { get; set; }
        [Description("页面内容")] public string PageContent { get; set; }
    }
}